package com.rm.freedrawsample;

import com.rm.freedrawsample.io.ValidatingObjectInputStream;
import com.rm.freedrawview.*;
import ohos.app.Context;
import ohos.app.Environment;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * Created by Riccardo on 30/05/2017.
 *
 * @author author
 * @version version
 */
public class FileHelper {

    private static final String TAG = "FileHelper";
    private static final String FILE_NAME = "draw_state.ser";

    /**
     * newSingleThreadExecutor .
     *
     * @return ExecutorService
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    }

    /**
     * saveStateIntoFile.
     *
     * @param context  context
     * @param state    state
     * @param listener listener
     */
    public static void saveStateIntoFile(
            final Context context, final FreeDrawSerializableState state,
            final StateSaveInterface listener) {
        if (context != null && state != null) {
            newSingleThreadExecutor().execute(new StateSaveRunnable(context, listener, state));
        } else {
            if (listener != null) {
                listener.onStateSaveError();
            }
        }
    }

    /**
     * getSavedStoreFromFile.
     *
     * @param context  context
     * @param listener listener
     */
    public static void getSavedStoreFromFile(
            final Context context, StateExtractorInterface listener) {
        if (context != null) {
            newSingleThreadExecutor().execute(new StateExtractorRunnable(context, listener));
        } else {
            if (listener != null) {
                listener.onStateExtractionError();
            }
        }
    }

    private static File getDrawStateFile(Context context) {
        File filesDir = new File(context.getFilesDir(), Environment.DIRECTORY_DOCUMENTS);
        if (filesDir.mkdirs()) {
            Log.i(TAG, "getDrawStateFile filesDir mkdirs");
        }
        File file = new File(filesDir, FILE_NAME);
        if (!file.exists()) {
            try {
                if (file.createNewFile()) {
                    Log.i(TAG, "getDrawStateFile file createNewFile");
                }
            } catch (IOException e) {
                Log.i(TAG, "getDrawStateFile " + e.getMessage());
            }
        }
        return file;
    }

    /**
     * deleteSavedStateFile.
     *
     * @param context context
     */
    public static void deleteSavedStateFile(Context context) {
        if (context != null) {
            File drawStateFile = getDrawStateFile(context);
            if (drawStateFile.exists()) {
                // drawStateFile.delete();
                try {
                    Files.delete(drawStateFile.toPath());
                } catch (IOException e) {
                    Log.i(TAG, "deleteSavedStateFile " + e.getMessage());
                }
            }
        }
    }


    private static class StateExtractorRunnable implements Runnable {

        private Context mContext;
        private StateExtractorInterface mListener;

        public StateExtractorRunnable(Context context, StateExtractorInterface listener) {
            mContext = context;
            mListener = listener;
        }

        @Override
        public void run() {
            FileInputStream fis = null;
            ValidatingObjectInputStream is = null;
            try {
                File drawStateFile = getDrawStateFile(mContext);
                fis = new FileInputStream(drawStateFile);
                // 代码检测
                // is = new ObjectInputStream(fis);
                is = new ValidatingObjectInputStream(fis);
                is.accept(FreeDrawSerializableState.class);
                is.accept(HistoryPath.class);
                is.accept(Point.class);
                is.accept(ResizeBehaviour.class);
                is.accept(Enum.class);
                is.accept(ArrayList.class);
                FreeDrawSerializableState state = (FreeDrawSerializableState) is.readObject();
                if (state != null) {
                    Log.i(TAG, "FreeDrawSerializableState " + state.toString());
                }
                if (mListener != null) {
                    runOnUiThread(() -> mListener.onStateExtracted(state));
                }
            } catch (Exception e) {
                Log.i(TAG, "error " + e.getMessage());
                if (mListener != null) {
                    runOnUiThread(() -> mListener.onStateExtractionError());
                }
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        Log.i(TAG, "StateExtractorRunnable " + e.getMessage());
                    }
                }
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        Log.i(TAG, "StateExtractorRunnable " + e.getMessage());
                    }
                }

            }
        }
    }


    private static class StateSaveRunnable implements Runnable {

        private final Context mContext;
        private final StateSaveInterface mListener;
        private final FreeDrawSerializableState mState;

        public StateSaveRunnable(
                Context context, StateSaveInterface listener, FreeDrawSerializableState state) {

            mContext = context;
            mListener = listener;
            mState = state;
        }

        @Override
        public void run() {
            FileOutputStream fos = null;
            ObjectOutputStream os = null;
            try {
                File drawStateFile = getDrawStateFile(mContext);
                fos = new FileOutputStream(drawStateFile);
                os = new ObjectOutputStream(fos);
                os.writeObject(mState);
                os.flush();
                fos.flush();
                if (mListener != null) {
                    runOnUiThread(() -> mListener.onStateSaved());
                }
            } catch (Exception e) {
                Log.i(TAG, e.getMessage());

                if (mListener != null) {
                    runOnUiThread(() -> mListener.onStateSaveError());
                }
            } finally {

                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        Log.i(TAG, "StateSaveRunnable " + e.getMessage());
                    }
                }
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        Log.i(TAG, "StateSaveRunnable " + e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * StateSaveInterface .
     */
    public interface StateSaveInterface {
        /**
         * onStateSaved.
         */
        void onStateSaved();

        /**
         * onStateSaveError.
         */
        void onStateSaveError();
    }

    /**
     * StateExtractorInterface.
     */
    public interface StateExtractorInterface {
        /**
         * onStateExtracted.
         *
         * @param state state
         */
        void onStateExtracted(FreeDrawSerializableState state);

        /**
         * onStateExtractionError.
         */
        void onStateExtractionError();
    }


    private static void runOnUiThread(Runnable runnable) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(runnable);
    }
}
